package top.buluoluo.apigateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import top.buluoluo.apigateway.service.TokenManagerService;
import top.buluoluo.commoncore.utils.JwtUtil;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Component
@Slf4j
public class JwtAuthenticationFilter implements WebFilter {

    private final JwtUtil jwtUtil;
    private final TokenManagerService tokenManagerService;

    @Autowired
    public JwtAuthenticationFilter(JwtUtil jwtUtil, TokenManagerService tokenManagerService) {
        this.jwtUtil = jwtUtil;
        this.tokenManagerService = tokenManagerService;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        // 获取全局请求ID，解决不知道为什么存在的触发2次过滤器
        String globalRequestId = exchange.getAttribute("GLOBAL_REQUEST_ID");
        if (globalRequestId == null) {
            // 如果没有全局ID，创建新的
            globalRequestId = UUID.randomUUID().toString();
            exchange.getAttributes().put("GLOBAL_REQUEST_ID", globalRequestId);
        }

        // 检查是否已被处理
        if (exchange.getAttribute("JWT_FILTER_PROCESSED") != null) {
            log.debug("[{}] 请求已被JWT过滤器处理过，直接跳过", globalRequestId);
            return chain.filter(exchange);
        }

        // 标记为已处理
        exchange.getAttributes().put("JWT_FILTER_PROCESSED", true);
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().value();
        log.info("[{}] JWT过滤器开始处理请求: {}", globalRequestId, path);
        String token = extractTokenFromRequest(request);

        if (token == null) {
            // 没有Token，不设置认证信息，直接放行，让spring security处理错误信息
            return chain.filter(exchange);
        }
        return validateToken(token, exchange)
                .flatMap(isValid -> {
                    if (!isValid) {
                        return sendErrorResponse(exchange, "Token无效或已过期");
                    }
                    // 将阻塞调用包装在 Mono.fromCallable 中
                    return Mono.fromCallable(() -> tokenManagerService.isTokenBlacklisted(token))
                            .flatMap(isBlacklisted -> {
                                if (isBlacklisted) {
                                    return sendErrorResponse(exchange, "Token已被拉黑，请重新登录");
                                }
                                // 修改为返回 Mono<Void>
                                return setAuthentication(token, exchange, chain);
                            });
                })
                .onErrorResume(e -> sendErrorResponse(exchange, "Token验证失败"));
    }

    private String extractTokenFromRequest(ServerHttpRequest request) {
        String bearerToken = request.getHeaders().getFirst("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }

    private Mono<Boolean> validateToken(String token, ServerWebExchange exchange) {
        String requestId = exchange.getAttribute("GLOBAL_REQUEST_ID");
        return Mono.fromCallable(() -> {
            log.info("[{}] 验证Token: {}", requestId, token);
            boolean isValid = jwtUtil.validateToken(token);
            log.info("[{}] Token验证结果: {}", requestId, isValid);
            return isValid;
        });
    }

    // 设置权限认证，返回 Mono<Void>
    private Mono<Void> setAuthentication(String token, ServerWebExchange exchange, WebFilterChain chain) {
        String globalRequestId = exchange.getAttribute("GLOBAL_REQUEST_ID");
        return Mono.fromCallable(() -> {
                    Long userId = jwtUtil.getUserIdFromToken(token);
                    List<GrantedAuthority> authorities = jwtUtil.getAuthoritiesFromToken(token);
                    log.info("[{}] 用户ID: {}", globalRequestId, userId);
                    log.info("[{}] 权限列表: {}", globalRequestId, authorities);
                    return new UsernamePasswordAuthenticationToken(userId, null, authorities);
                })
                .flatMap(authentication -> {
                    // 添加用户信息到请求头
                    ServerHttpRequest mutatedRequest = exchange.getRequest().mutate()
                            .header("X-User-Id", authentication.getName())
                            .header("X-User-Roles", authentication.getAuthorities().stream()
                                    .map(GrantedAuthority::getAuthority)
                                    .collect(Collectors.joining(",")))
                            .build();
                    ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
                    // 复制所有原有属性
                    mutatedExchange.getAttributes().putAll(exchange.getAttributes());

                    log.info("[{}] 设置安全上下文并继续过滤器链", globalRequestId);

                    // 唯一的过滤器链调用
                    return chain.filter(mutatedExchange)
                            .contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));
                });
    }

    private Mono<Void> sendErrorResponse(ServerWebExchange exchange, String message) {
        ServerHttpResponse response = exchange.getResponse();
        //检查响应是否已提交
        if (response.isCommitted()) {
            return Mono.empty();
        }
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        String jsonResponse = String.format("{\"code\":401,\"message\":\"%s\"}", message);
        byte[] bytes = jsonResponse.getBytes(StandardCharsets.UTF_8);

        return response.writeWith(Mono.just(response.bufferFactory().wrap(bytes)));
    }
}